home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1996 June
/
EnigmA AMIGA RUN 08 (1996)(G.R. Edizioni)(IT)[!][issue 1996-06][EARSAN CD VII].iso
/
earcd
/
dbase
/
kf2ex.lha
/
KingFisher-Distribution
/
Developer
/
kf-api.doc
< prev
next >
Wrap
Text File
|
1995-09-06
|
19KB
|
523 lines
KingFisher Database Server (KFServer)
Application Programming Interface (API)
Copyright © 1993,1994,1995 Udo K. Schuermann
All rights reserved
---------------------------------------------------------------------------
INTRODUCTION
---------------------------------------------------------------------------
The KFServer's API is based on the Amiga's Exec messages. This is a very
efficient means of transporting potentially enormous amounts of information
between multiple tasks. The only way for a client application to talk to
the KFServer is by sending messages to the KFServer and waiting for these
to be returned.
All the gory details of this process are handled for you by the "kf-api.c"
file, providing you with simple functions to setup messages, fill the
parameter fields, send the message, wait for a reply from the KFServer, and
then return you the necessary results.
The "kf-api.c" functions were developed using SAS/C 6.55 but should
be easily compiled with other compilers, too.
---------------------------------------------------------------------------
GETTING STARTED
---------------------------------------------------------------------------
The first thing you should do is compile "kf-api.c" to produce "kf-api.o"
(i.e. don't link this file with anything; you'll use the functions in the
object (.o) file later to link with your project.) Once you have this file
compiled you are ready to begin your project!
The basic outline of a KFServer Client Application is this:
Handle = KFLogin( "Identifier" )
IF (Handle = NULL) THEN EXIT( "Server Not Running" )
REPEAT
Issue command
IF Successful THEN ECHO Results
UNTIL Finished
KFLogout( Handle )
1. An application must always login to the server at least once. The
"Handle" is the identifier passed to every API function, to identify
this "connection" to the server. If you examine the "ReOrder.c" tool,
you will notice that TWO (2) handles are obtained, one for the input
database, and one for the output database.
There is no limit (except with the unregistered version) to the
number of handles your software may obtain. Each such handle is
associated with a database and maintains its own, distinct set of
parameters. This provides simultaneous access to multiple databases.
2. The KFServer is a single-threaded application, which means it processes
each request as it comes in and goes on to the next request only when
the previous one has completed and has been returned to the client.
This means that certain lengthy operations (such as a database reindex
operation) could tie up the server for several minutes, thereby blocking
all access by other clients.
To somewhat lessen the impact of this potentially very frustrating
blockage, these lengthy operations return to the caller periodically
with the implication that the function is called again at once until the
KFServer finally returns with an "all done," whereupon the function is
completed.
These interruptions provide brief pauses during which other clients
may get a few requests processed. It is also recommended that a client
lower its priority by one before embarking on such a lengthy procedure.
This can greatly improve response time for other clients, should there
be any!
The "kf-api.c" function for reindexing, for example, takes care of
the repeated calling, so there is no need for you to directly concern
yourself with this aspect unless you should wonder why your other client
software is slowing down dramatically during a reindexing operation.
3. Always call KFLogout() on a handle you have obtained. If you are using
SAS/C 6.50+ and the kf-api.c file has the #define USEAUTO enabled (which
it is, by default) then the kf-api.c will automatically clear all open
handles for you when you exit, but it is not recommended to rely on
this.
---------------------------------------------------------------------------
EXAMPLE OF USING THE KF-API.C
---------------------------------------------------------------------------
Here is a very small application you can play with:
#include <stdio.h>
#include "kf-api.h"
main() {
struct KFMsg *HANDLE = KFLogin("My Mini Project");
if( HANDLE ) {
unsigned long MaxClients;
char *DefaultDB;
MaxClients = KFMaxClients( HANDLE );
printf("This KFServer supports %ld clients,\n",MaxClients);
DefaultDB = KFCurDatabaseName( HANDLE );
printf("and serves \"%s\" as the default database.\n",DefaultDB);
KFLogout( HANDLE );
/* at this time HANDLE is invalid */
} else
printf("Cannot login!\n");
} /* main() */
This application makes use of the following API functions:
KFLogin()
KFLogout()
KFMaxClients()
KFCurDatabaseName()
These functions are described in detail in the section below.
---------------------------------------------------------------------------
FUNCTIONS IN THE API:
---------------------------------------------------------------------------
struct KFMsg *KFLogin( char *Identifier );
This should almost always be the first function you call. It
returns you the handle which you use in all subsequent calls. The
parameter to this function should be some meaningful identification
and may even be NULL. It is highly recommended to somehow identify
your application, as in the example above. The KFServer does NOT
make a copy of this string, so the address must continue to live as
long as the handle is active.
If the function returns NULL, then login failed. There is more
than one reason for this. As there is no message with error field
that is returned by this function, examine (but do not modify!) the
variables
UWORD AsyncErr
and
char *AsyncMsg
for an error code and some additional explanation. The most likely
causes of a KFLogin() call failing is that either the KFServer is
out of handles (never the case with the registered version) or the
KFServer is not running and could not be started successfully.
BOOL KFLogout( struct KFMsg *Msg );
Always call this function with every handle obtained through the
KFLogin() call. It is legal to call this function with a NULL
handle, but the function will return FALSE in that case.
The function returns TRUE if the logout succeeded. In either case,
the handle is invalid after this call.
BOOL KFStatus( struct KFMsg *Msg );
Obtains information about the currently attached client, including
the current database in use, the current record position in the
database, the version of the running server, etc. This information
is returned in the handle's .BParam field.
EX: if( KFStatus( HANDLE ) )
printf("%s\n",HANDLE->BParam);
NOTE: In a future version, this function will not require a prior
KFLogin() and may be used to obtain some simple information
about the KFServer (which will be started if not running.)
ULONG KFMaxClients( struct KFMsg *Msg );
Obtains the maximum number of clients that the currently running
KFServer supports. Depending on how many handles have already been
handed out by the KFServer to various clients, the actual number of
available handles may be less than the maximum. This should be of
concern only with unregistered versions.
EX: ULONG MaxClients = KFMaxClients( HANDLE );
NOTE: It is LEGAL to call KFMaxClients() with a NULL handle!
This will actually create a temporary handle and call the
KFServer with that. If the KFServer is configured with the
"keep-running=no" parameter, however, the KFServer will
NOT, in this case, terminate again, but remain operational
with 0 clients attached. Auto termination of the KFServer
functions only during an actual logout procedure.
ULONG KFCurFish( struct KFMsg *Msg );
Obtains the current position in the database which is a number 1 or
greater. If this function returns 0, then the database is empty
and no record or record-specific data may be obtained from it.
EX: ULONG CurFish = KFCurFish( HANDLE );
if( CurFish == 0 )
printf("We have no data!\n");
else
printf("Record #%ld is the current one.\n",CurFish);
NOTE: The .FParam field is also filled with the current flags
(see KFCurFlags() function below.)
UWORD KFCurFlags( struct KFMsg *Msg );
Obtains the flags of the current record in the database. Eight of
these flags are pre-defined, while another eight are application or
user-specific.
UWORD CurFlags = KFCurFlags( HANDLE );
printf("System flags: %02x, User flags: %02x\n",
CurFlags >> 8,
CurFlags & 0x0f);
NOTE: The KFCurFish() command (see above) provides the current
flags as a sideeffect in the handle's .FParam field.
ULONG KFCurDisk( struct KFMsg *Msg );
Obtains the current disk number with which the current record is
associated. Note, that a single-volume software collection, such
as found on a CD-ROM, is likely to be organized as a single disk,
wherefore the disk number is always 1.
char *KFCurDatabaseName( struct KFMsg *Msg );
Obtains the .kfdb filename of the current database.
NOTE: The value returned is actually the same pointer as the
handle's .Buffer, wherefore the value will not survive the
next use of the handle when passed to the KFServer again.
If you require this value, then strcpy() it to a different
buffer!
ULONG KFCurDatabaseSize( struct KFMsg *Msg );
Returns the number of records in the current database. If the
function returns 0, then the database is empty.
BOOL KFSelectFish( struct KFMsg *Msg, ULONG FishNumber );
Selects a different record in the database for future operations.
The record (FishNumber) given must be 1 or greater, and must not
exceed the total number of records in the database (as returned by
a call to KFCurDatabaseSize().)
ULONG KFNextVersion( struct KFMsg *Msg );
ULONG KFPrevVersion( struct KFMsg *Msg );
Returns the record number of the next/previous version as stored in
the current database's index. If this function returns 0, then no
next/previous version is known.
BOOL KFListDatabases( struct KFMsg *Msg );
This call returns a pointer to the handle's .Buffer where the
KFServer has stored a list of all known .kfdb files, along with the
long, human-readable descriptions found within these. The format
of the buffer is:
Descriptive database name
\1
.kfdb name
\n
This is repeated for each existing .kfdb file and the buffer is
then terminated with a \0. Notice that \1 is an ASCII 01 (^A)
symbol, and \n is a newline.
Here is an example without the linebreaks used above to make the
components stand out more distinctly:
Fresh Fish® Database\1FreshFish.kfdb\n
Original Fish Disks\11000Fish.kfdb\n
\0
BOOL KFSelectDatabase( struct KFMsg *Msg, char *Filename );
Ask the KFServer to close the current database and switch to the
new database whose name (a .kfdb file) is given by the Filename
parameter. The 1st record in the database becomes the current
record. Clients may wish to restore an old, saved position with a
KFSelectFish() call or directly load a record with a KFGetFish()
call.
The command returns FALSE if the new database could not be opened,
in which case the current database is not closed. If the function
returns TRUE, the .BParam contains a bitmask to indicate the R/W
status of various database components. If the entire value is 0,
(equal to a boolean FALSE) then no portion of the database can be
modified. AND'ing the value with the following gives details on
each component:
x AND 1 The main database index
x AND 2 The quick index
x AND 4 The database records
If the database is not writable, any additions to it, or changes
to the index (i.e. flags) can, and most likely will, cause error
messages from the server when the database is closed.
NOTE: The Filename must be terminated either by a \0 or \n.
This makes it easier to point directly at a name in the
.Buffer (see KFListDatabases() command, above.)
NOTE: In previous versions the .BParam value was a simple
boolean to indicate read/write status of the entire
database based solely on the main index, which was not
always correct, especially when only the main index
resided on writable medium.
BOOL KFGetFish( struct KFMsg *Msg, ULONG FishNumber );
Retrieves a specific record from the current database. If the
FishNumber is 0 (rather than a valid record number 1 or greater)
then the current record is retrieved, whichever that may be.
NOTE: The .BParam field returns the actual record number
retrieved (useful when called with a record of 0)
The .DParam field returns the disk with which this record
is associated
The .FParam field returns the record's associated flags
(see the KFCurFlags() command)
The .FInfo field returns situational information on this
record, indicating if previous or next versions
exist, or if this is the first or last record or
disk in the database. This field represents a mask
of bits, which are defined in the kf.h file.
BOOL KFSetFlag( struct KFMsg *Msg, UWORD Mask );
BOOL KFClrFlag( struct KFMsg *Msg, UWORD Mask );
Sets/clears flags in the indicated Mask. A Mask of 0xffff would
set/clear all flags. A Mask of 0x0000 has no effect at all. You
should not call this function if the call to KFSelectDatabase()
returned a 0 in the .BParam field, indicating that the database
cannot be modified.
BOOL KFFindFlag( struct KFMsg *Msg, BOOL Forward, UWORD MatchMask, UWORD AvoidMask );
Starting with the current record, the KFServer will make current
the next or previous record matching the given masks. If
successful, the function returns TRUE and the new record number is
returned in the .BParam field.
An AvoidMask of 0xffff will NEVER produce a match (as all records
with any flag are avoided), while a MatchMask of 0x0000 will
likewise NEVER produce a match.
The Forward variable determines the direction in which the search
is performed. The KFServer can search thousands of records in a
fraction of a second.
BOOL KFGetDisk( struct KFMsg *Msg, ULONG DiskNumber );
Retrieves the first record associated with the indicated disk,
provided this disk exists (in which case the function returns TRUE)
and an implicit KFGetFish() call has been performed (see above.)
BOOL KFNextFish( struct KFMsg *Msg );
BOOL KFPrevFish( struct KFMsg *Msg );
Moves to the next/previous record in the database, returning TRUE
if such a record existed. No retrieval of data is performed.
BOOL KFParseFile( struct KFMsg *Msg, char *Filename );
BOOL KFEndParsing( struct KFMsg *Msg );
KFParseFile() is called both to begin and to continue parsing a
file for records in the Product Info Specification format. Each
time the function returns either TRUE or FALSE. If TRUE, the
.Buffer contains a record extracted from the file, which may be
passed to the KFAddFish() function if so desired.
Once this function returns FALSE, or the .Error field contains
kfeNOMORE, the file has been completely parsed for all records.
Only if you wish to terminate parsing before the file has been
completely scanned, may you call KFEndParsing().
EX: BOOL HaveAdded = FALSE;
for(;;) {
if( KFParseFile( HANDLE, "myfile" ) ) {
printf("%s\n\n***** Add this record? (Y/N/Q) ",
HANDLE->Buffer);
/* get Yes/No/Quit response */
if( Yes )
if( KFAddFish( HANDLE, 1, 0x0000, -1, -1 ))
HaveAdded = TRUE;
else {
printf("Cannot add to the database!\n");
break;
}
else
if( No )
continue;
else
if( Quit ) {
break;
}
} else {
HaveAdded = FALSE; /* KFServer knows it's done */
break;
}
} /* for */
if( HaveAdded )
KFEndAdding( HANDLE );
BOOL KFAddFish( struct KFMsg *Msg, UWORD Disk, UWORD Flags, LONG PrevVersion, LONG NextVersion );
BOOL KFEndAdding( struct KFMsg *Msg );
When KFAddFish() is first called, it attempts to lock the current
database for exclusive access. If more than one handle to this
database has been given out (even if both are in your "possession")
the call will fail.
Once you have added all the records you wish to add, call the
KFEndAdding() function to release the database again to everyone
else.
For an example of this function, see the source code example above.
BOOL KFReindex( struct KFMsg *Msg );
This function attempts to lock the current database for exclusive
access. If it succeeds, it will proceed to reindex the database,
thereby rebuilding all related index files based solely on the data
found in the database files.
Once the function returns, the database has been released again and
the index is up to date. Notice that this function may require
much time to perform its duty, during which other database clients
may experience EXTREME drop in performance. For this reason, it is
STRONGLY RECOMMENDED to lower your process priority before calling
this function, and raise it again once completed.
Previously mentioned problems with the reindexing operation have
been corrected, to the best of my knowledge and experience.
---------------------------------------------------------------------------
HINTS AND TIPS
---------------------------------------------------------------------------
1. Use the functions provided by the API as much as possible. It is LEGAL
to access the fields of the handle, so long as their use is documented
here. Do not rely on apparently reliable behavior if no mention is made
of it here. If you must know, ask me. It may always be an omission.
If you need something added, let me know and I will see what I can do.
2. You may modify the KFAPIServerName[128] array to provide a different
name of the KFServer binary, perhaps with a full path added. This is
the file which the API attempts to start when the KFServer's message
port cannot be located.
KingFisher copies the value of the SERVERNAME tooltype to this
variable.
If you wish to suppress startup messages and console windows for the
cleanest look, set the KFAPISilentRun to TRUE.
KingFisher does this if it finds the NOOUTPUT tooltype.
Never EVER rely on anything else in the kf-api.c file, as nothing else
will be guaranteed and behavior may dramatically change.
3. If so desired, you may use the CallServer() function, which expects a
fully initialized handle. The use of CallServer() is documented in the
"kf-api.c" by itself and all the functions that use it.
4. If you experience problems, please let me know. The more detail you can
give, the more likely it is that I can help you.
5. While the file above may not always be up to date, you can get more
detail (although fewer examples) on the exact message port interface
from the kf.h file.
Enjoy!
._. Udo Schuermann Snail mail: 7022 Hanover Parkway, Apt. C2
( ) walrus@wam.umd.edu Greenbelt, MD 20770-2049
#EOT